{
  "nbformat": 4,
  "nbformat_minor": 0,
  "metadata": {
    "colab": {
      "name": "feature_crosses.ipynb",
      "version": "0.3.2",
      "views": {},
      "default_view": {},
      "provenance": [],
      "collapsed_sections": [
        "ZTDHHM61NPTw",
        "0i7vGo9PTaZl",
        "copyright-notice"
      ]
    },
    "kernelspec": {
      "name": "python3",
      "display_name": "Python 3"
    }
  },
  "cells": [
    {
      "source": [
        "#### Copyright 2017 Google LLC."
      ],
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "copyright-notice"
      }
    },
    {
      "outputs": [],
      "source": [
        "# Licensed under the Apache License, Version 2.0 (the \"License\");\n",
        "# you may not use this file except in compliance with the License.\n",
        "# You may obtain a copy of the License at\n",
        "#\n",
        "# https://www.apache.org/licenses/LICENSE-2.0\n",
        "#\n",
        "# Unless required by applicable law or agreed to in writing, software\n",
        "# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
        "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
        "# See the License for the specific language governing permissions and\n",
        "# limitations under the License."
      ],
      "cell_type": "code",
      "metadata": {
        "cellView": "both",
        "colab": {
          "autoexec": {
            "startup": false,
            "wait_interval": 0
          }
        },
        "colab_type": "code",
        "id": "copyright-notice2"
      },
      "execution_count": 0
    },
    {
      "metadata": {
        "id": "g4T-_IsVbweU",
        "colab_type": "text"
      },
      "cell_type": "markdown",
      "source": [
        " # Croisements de caract\u00e9ristiques"
      ]
    },
    {
      "metadata": {
        "id": "F7dke6skIK-k",
        "colab_type": "text"
      },
      "cell_type": "markdown",
      "source": [
        " **Objectifs d'apprentissage\u00a0:**\n",
        "  * Am\u00e9liorer un mod\u00e8le de r\u00e9gression lin\u00e9aire en ajoutant des caract\u00e9ristiques synth\u00e9tiques (suite de l'exercice pr\u00e9c\u00e9dent)\n",
        "  * Utiliser la fonction d'entr\u00e9e pour convertir des objets Pandas `DataFrame` en `Tensors`, et invoquer la fonction d'entr\u00e9e dans les op\u00e9rations `fit()` et `predict()`\n",
        "  * Utiliser l'algorithme d'optimisation FTRL pour l'apprentissage de mod\u00e8le\n",
        "  * Cr\u00e9er des caract\u00e9ristiques synth\u00e9tiques au moyen de l'encodage one-hot, du binning et des croisements de caract\u00e9ristiques"
      ]
    },
    {
      "metadata": {
        "id": "NS_fcQRd8B97",
        "colab_type": "text"
      },
      "cell_type": "markdown",
      "source": [
        " ## Configuration"
      ]
    },
    {
      "metadata": {
        "id": "4IdzD8IdIK-l",
        "colab_type": "text"
      },
      "cell_type": "markdown",
      "source": [
        " Comme pour les exercices pr\u00e9c\u00e9dents, vous allez commencer par d\u00e9finir l'entr\u00e9e et par cr\u00e9er le code de chargement de donn\u00e9es."
      ]
    },
    {
      "metadata": {
        "id": "CsfdiLiDIK-n",
        "colab_type": "code",
        "colab": {
          "autoexec": {
            "startup": false,
            "wait_interval": 0
          }
        }
      },
      "source": [
        "from __future__ import print_function\n",
        "\n",
        "import math\n",
        "\n",
        "from IPython import display\n",
        "from matplotlib import cm\n",
        "from matplotlib import gridspec\n",
        "from matplotlib import pyplot as plt\n",
        "import numpy as np\n",
        "import pandas as pd\n",
        "from sklearn import metrics\n",
        "import tensorflow as tf\n",
        "from tensorflow.python.data import Dataset\n",
        "\n",
        "tf.logging.set_verbosity(tf.logging.ERROR)\n",
        "pd.options.display.max_rows = 10\n",
        "pd.options.display.float_format = '{:.1f}'.format\n",
        "\n",
        "california_housing_dataframe = pd.read_csv(\"https://download.mlcc.google.com/mledu-datasets/california_housing_train.csv\", sep=\",\")\n",
        "\n",
        "california_housing_dataframe = california_housing_dataframe.reindex(\n",
        "    np.random.permutation(california_housing_dataframe.index))"
      ],
      "cell_type": "code",
      "execution_count": 0,
      "outputs": []
    },
    {
      "metadata": {
        "id": "10rhoflKIK-s",
        "colab_type": "code",
        "colab": {
          "autoexec": {
            "startup": false,
            "wait_interval": 0
          }
        }
      },
      "source": [
        "def preprocess_features(california_housing_dataframe):\n",
        "  \"\"\"Prepares input features from California housing data set.\n",
        "\n",
        "  Args:\n",
        "    california_housing_dataframe: A Pandas DataFrame expected to contain data\n",
        "      from the California housing data set.\n",
        "  Returns:\n",
        "    A DataFrame that contains the features to be used for the model, including\n",
        "    synthetic features.\n",
        "  \"\"\"\n",
        "  selected_features = california_housing_dataframe[\n",
        "    [\"latitude\",\n",
        "     \"longitude\",\n",
        "     \"housing_median_age\",\n",
        "     \"total_rooms\",\n",
        "     \"total_bedrooms\",\n",
        "     \"population\",\n",
        "     \"households\",\n",
        "     \"median_income\"]]\n",
        "  processed_features = selected_features.copy()\n",
        "  # Create a synthetic feature.\n",
        "  processed_features[\"rooms_per_person\"] = (\n",
        "    california_housing_dataframe[\"total_rooms\"] /\n",
        "    california_housing_dataframe[\"population\"])\n",
        "  return processed_features\n",
        "\n",
        "def preprocess_targets(california_housing_dataframe):\n",
        "  \"\"\"Prepares target features (i.e., labels) from California housing data set.\n",
        "\n",
        "  Args:\n",
        "    california_housing_dataframe: A Pandas DataFrame expected to contain data\n",
        "      from the California housing data set.\n",
        "  Returns:\n",
        "    A DataFrame that contains the target feature.\n",
        "  \"\"\"\n",
        "  output_targets = pd.DataFrame()\n",
        "  # Scale the target to be in units of thousands of dollars.\n",
        "  output_targets[\"median_house_value\"] = (\n",
        "    california_housing_dataframe[\"median_house_value\"] / 1000.0)\n",
        "  return output_targets"
      ],
      "cell_type": "code",
      "execution_count": 0,
      "outputs": []
    },
    {
      "metadata": {
        "id": "ufplEkjN8KUp",
        "colab_type": "code",
        "colab": {
          "autoexec": {
            "startup": false,
            "wait_interval": 0
          }
        }
      },
      "source": [
        "# Choose the first 12000 (out of 17000) examples for training.\n",
        "training_examples = preprocess_features(california_housing_dataframe.head(12000))\n",
        "training_targets = preprocess_targets(california_housing_dataframe.head(12000))\n",
        "\n",
        "# Choose the last 5000 (out of 17000) examples for validation.\n",
        "validation_examples = preprocess_features(california_housing_dataframe.tail(5000))\n",
        "validation_targets = preprocess_targets(california_housing_dataframe.tail(5000))\n",
        "\n",
        "# Double-check that we've done the right thing.\n",
        "print(\"Training examples summary:\")\n",
        "display.display(training_examples.describe())\n",
        "print(\"Validation examples summary:\")\n",
        "display.display(validation_examples.describe())\n",
        "\n",
        "print(\"Training targets summary:\")\n",
        "display.display(training_targets.describe())\n",
        "print(\"Validation targets summary:\")\n",
        "display.display(validation_targets.describe())"
      ],
      "cell_type": "code",
      "execution_count": 0,
      "outputs": []
    },
    {
      "metadata": {
        "id": "oJlrB4rJ_2Ma",
        "colab_type": "code",
        "colab": {
          "autoexec": {
            "startup": false,
            "wait_interval": 0
          }
        }
      },
      "source": [
        "def construct_feature_columns(input_features):\n",
        "  \"\"\"Construct the TensorFlow Feature Columns.\n",
        "\n",
        "  Args:\n",
        "    input_features: The names of the numerical input features to use.\n",
        "  Returns:\n",
        "    A set of feature columns\n",
        "  \"\"\"\n",
        "  return set([tf.feature_column.numeric_column(my_feature)\n",
        "              for my_feature in input_features])"
      ],
      "cell_type": "code",
      "execution_count": 0,
      "outputs": []
    },
    {
      "metadata": {
        "id": "NBxoAfp2AcB6",
        "colab_type": "code",
        "colab": {
          "autoexec": {
            "startup": false,
            "wait_interval": 0
          }
        }
      },
      "source": [
        "def my_input_fn(features, targets, batch_size=1, shuffle=True, num_epochs=None):\n",
        "    \"\"\"Trains a linear regression model.\n",
        "  \n",
        "    Args:\n",
        "      features: pandas DataFrame of features\n",
        "      targets: pandas DataFrame of targets\n",
        "      batch_size: Size of batches to be passed to the model\n",
        "      shuffle: True or False. Whether to shuffle the data.\n",
        "      num_epochs: Number of epochs for which data should be repeated. None = repeat indefinitely\n",
        "    Returns:\n",
        "      Tuple of (features, labels) for next data batch\n",
        "    \"\"\"\n",
        "    \n",
        "    # Convert pandas data into a dict of np arrays.\n",
        "    features = {key:np.array(value) for key,value in dict(features).items()}                                           \n",
        " \n",
        "    # Construct a dataset, and configure batching/repeating.\n",
        "    ds = Dataset.from_tensor_slices((features,targets)) # warning: 2GB limit\n",
        "    ds = ds.batch(batch_size).repeat(num_epochs)\n",
        "    \n",
        "    # Shuffle the data, if specified.\n",
        "    if shuffle:\n",
        "      ds = ds.shuffle(10000)\n",
        "    \n",
        "    # Return the next batch of data.\n",
        "    features, labels = ds.make_one_shot_iterator().get_next()\n",
        "    return features, labels"
      ],
      "cell_type": "code",
      "execution_count": 0,
      "outputs": []
    },
    {
      "metadata": {
        "id": "hweDyy31LBsV",
        "colab_type": "text"
      },
      "cell_type": "markdown",
      "source": [
        " ## Algorithme d'optimisation FTRL\n",
        "\n",
        "Les mod\u00e8les lin\u00e9aires de grande dimension tirent parti de l'utilisation d'une variante de l'optimisation bas\u00e9e sur le gradient, appel\u00e9e FTRL. L'avantage de cet algorithme est de permettre une mise \u00e0 l'\u00e9chelle du taux d'apprentissage qui diff\u00e8re en fonction du coefficient, ce qui peut se r\u00e9v\u00e9ler particuli\u00e8rement utile si certaines caract\u00e9ristiques utilisent rarement des valeurs non\u00a0nulles (il est aussi parfaitement adapt\u00e9 \u00e0 la r\u00e9gularisation\u00a0L1). L'algorithme FTRL peut \u00eatre appliqu\u00e9 \u00e0 l'aide de la classe [FtrlOptimizer](https://www.tensorflow.org/api_docs/python/tf/train/FtrlOptimizer)."
      ]
    },
    {
      "metadata": {
        "id": "S0SBf1X1IK_O",
        "colab_type": "code",
        "colab": {
          "autoexec": {
            "startup": false,
            "wait_interval": 0
          }
        }
      },
      "source": [
        "def train_model(\n",
        "    learning_rate,\n",
        "    steps,\n",
        "    batch_size,\n",
        "    feature_columns,\n",
        "    training_examples,\n",
        "    training_targets,\n",
        "    validation_examples,\n",
        "    validation_targets):\n",
        "  \"\"\"Trains a linear regression model.\n",
        "  \n",
        "  In addition to training, this function also prints training progress information,\n",
        "  as well as a plot of the training and validation loss over time.\n",
        "  \n",
        "  Args:\n",
        "    learning_rate: A `float`, the learning rate.\n",
        "    steps: A non-zero `int`, the total number of training steps. A training step\n",
        "      consists of a forward and backward pass using a single batch.\n",
        "    feature_columns: A `set` specifying the input feature columns to use.\n",
        "    training_examples: A `DataFrame` containing one or more columns from\n",
        "      `california_housing_dataframe` to use as input features for training.\n",
        "    training_targets: A `DataFrame` containing exactly one column from\n",
        "      `california_housing_dataframe` to use as target for training.\n",
        "    validation_examples: A `DataFrame` containing one or more columns from\n",
        "      `california_housing_dataframe` to use as input features for validation.\n",
        "    validation_targets: A `DataFrame` containing exactly one column from\n",
        "      `california_housing_dataframe` to use as target for validation.\n",
        "      \n",
        "  Returns:\n",
        "    A `LinearRegressor` object trained on the training data.\n",
        "  \"\"\"\n",
        "\n",
        "  periods = 10\n",
        "  steps_per_period = steps / periods\n",
        "\n",
        "  # Create a linear regressor object.\n",
        "  my_optimizer = tf.train.FtrlOptimizer(learning_rate=learning_rate)\n",
        "  my_optimizer = tf.contrib.estimator.clip_gradients_by_norm(my_optimizer, 5.0)\n",
        "  linear_regressor = tf.estimator.LinearRegressor(\n",
        "      feature_columns=feature_columns,\n",
        "      optimizer=my_optimizer\n",
        "  )\n",
        "  \n",
        "  training_input_fn = lambda: my_input_fn(training_examples, \n",
        "                                          training_targets[\"median_house_value\"], \n",
        "                                          batch_size=batch_size)\n",
        "  predict_training_input_fn = lambda: my_input_fn(training_examples, \n",
        "                                                  training_targets[\"median_house_value\"], \n",
        "                                                  num_epochs=1, \n",
        "                                                  shuffle=False)\n",
        "  predict_validation_input_fn = lambda: my_input_fn(validation_examples, \n",
        "                                                    validation_targets[\"median_house_value\"], \n",
        "                                                    num_epochs=1, \n",
        "                                                    shuffle=False)\n",
        "\n",
        "  # Train the model, but do so inside a loop so that we can periodically assess\n",
        "  # loss metrics.\n",
        "  print(\"Training model...\")\n",
        "  print(\"RMSE (on training data):\")\n",
        "  training_rmse = []\n",
        "  validation_rmse = []\n",
        "  for period in range (0, periods):\n",
        "    # Train the model, starting from the prior state.\n",
        "    linear_regressor.train(\n",
        "        input_fn=training_input_fn,\n",
        "        steps=steps_per_period\n",
        "    )\n",
        "    # Take a break and compute predictions.\n",
        "    training_predictions = linear_regressor.predict(input_fn=predict_training_input_fn)\n",
        "    training_predictions = np.array([item['predictions'][0] for item in training_predictions])\n",
        "    validation_predictions = linear_regressor.predict(input_fn=predict_validation_input_fn)\n",
        "    validation_predictions = np.array([item['predictions'][0] for item in validation_predictions])\n",
        "    \n",
        "    # Compute training and validation loss.\n",
        "    training_root_mean_squared_error = math.sqrt(\n",
        "        metrics.mean_squared_error(training_predictions, training_targets))\n",
        "    validation_root_mean_squared_error = math.sqrt(\n",
        "        metrics.mean_squared_error(validation_predictions, validation_targets))\n",
        "    # Occasionally print the current loss.\n",
        "    print(\"  period %02d : %0.2f\" % (period, training_root_mean_squared_error))\n",
        "    # Add the loss metrics from this period to our list.\n",
        "    training_rmse.append(training_root_mean_squared_error)\n",
        "    validation_rmse.append(validation_root_mean_squared_error)\n",
        "  print(\"Model training finished.\")\n",
        "\n",
        "  \n",
        "  # Output a graph of loss metrics over periods.\n",
        "  plt.ylabel(\"RMSE\")\n",
        "  plt.xlabel(\"Periods\")\n",
        "  plt.title(\"Root Mean Squared Error vs. Periods\")\n",
        "  plt.tight_layout()\n",
        "  plt.plot(training_rmse, label=\"training\")\n",
        "  plt.plot(validation_rmse, label=\"validation\")\n",
        "  plt.legend()\n",
        "\n",
        "  return linear_regressor"
      ],
      "cell_type": "code",
      "execution_count": 0,
      "outputs": []
    },
    {
      "metadata": {
        "id": "1Cdr02tLIK_Q",
        "colab_type": "code",
        "colab": {
          "autoexec": {
            "startup": false,
            "wait_interval": 0
          }
        }
      },
      "source": [
        "_ = train_model(\n",
        "    learning_rate=1.0,\n",
        "    steps=500,\n",
        "    batch_size=100,\n",
        "    feature_columns=construct_feature_columns(training_examples),\n",
        "    training_examples=training_examples,\n",
        "    training_targets=training_targets,\n",
        "    validation_examples=validation_examples,\n",
        "    validation_targets=validation_targets)"
      ],
      "cell_type": "code",
      "execution_count": 0,
      "outputs": []
    },
    {
      "metadata": {
        "id": "i4lGvqajDWlw",
        "colab_type": "text"
      },
      "cell_type": "markdown",
      "source": [
        " ## Encodage one-hot pour les caract\u00e9ristiques discr\u00e8tes\n",
        "\n",
        "Les caract\u00e9ristiques discr\u00e8tes (cha\u00eenes, \u00e9num\u00e9rations et entiers) sont g\u00e9n\u00e9ralement converties en familles de caract\u00e9ristiques binaires avant d'apprendre un mod\u00e8le de r\u00e9gression logistique.\n",
        "\n",
        "Supposons que vous ayez cr\u00e9\u00e9 une caract\u00e9ristique synth\u00e9tique pouvant utiliser la valeur `0`, `1` ou `2`, et qu'il existe quelques points d'apprentissage\u00a0:\n",
        "\n",
        "| # | feature_value |\n",
        "|---|---------------|\n",
        "| 0 |             2 |\n",
        "| 1 |             0 |\n",
        "| 2 |             1 |\n",
        "\n",
        "Pour chaque valeur cat\u00e9gorique possible, vous cr\u00e9ez une caract\u00e9ristique **binaire** de **valeurs r\u00e9elles** pouvant utiliser une seule des deux valeurs possibles\u00a0: 1.0 si l'exemple contient cette valeur et 0.0 dans le cas contraire. Dans l'exemple ci-dessus, la caract\u00e9ristique cat\u00e9gorique est convertie en trois caract\u00e9ristiques, et les points d'apprentissage se pr\u00e9sentent d\u00e9sormais comme suit\u00a0:\n",
        "\n",
        "| # | feature_value_0 | feature_value_1 | feature_value_2 |\n",
        "|---|-----------------|-----------------|-----------------|\n",
        "| 0 |             0.0 |             0.0 |             1.0 |\n",
        "| 1 |             1.0 |             0.0 |             0.0 |\n",
        "| 2 |             0.0 |             1.0 |             0.0 |"
      ]
    },
    {
      "metadata": {
        "id": "KnssXowblKm7",
        "colab_type": "text"
      },
      "cell_type": "markdown",
      "source": [
        " ## Caract\u00e9ristiques binaires\n",
        "\n",
        "La caract\u00e9ristique `population` peut \u00eatre r\u00e9partie dans les trois\u00a0cat\u00e9gories suivantes (par exemple)\u00a0:\n",
        "- `cat\u00e9gorie_0` (`< 5000`)\u00a0: correspondant aux \u00eelots urbains les moins peupl\u00e9s\n",
        "- `cat\u00e9gorie_1` (`5000 - 25000`)\u00a0: correspondant aux \u00eelots urbains moyennement peupl\u00e9s\n",
        "- `cat\u00e9gorie_2` (`> 25000`)\u00a0:  correspondant aux \u00eelots urbains dens\u00e9ment peupl\u00e9s\n",
        "\n",
        "Compte tenu des d\u00e9finitions de cat\u00e9gories ci-dessus, le vecteur `population` suivant\u00a0:\n",
        "\n",
        "    [[10001], [42004], [2500], [18000]]\n",
        "\n",
        "devient le vecteur de caract\u00e9ristiques binaire\u00a0:\n",
        "\n",
        "    [[1], [2], [0], [1]]\n",
        "\n",
        "Les valeurs de caract\u00e9ristique sont d\u00e9sormais les index de cat\u00e9gorie. Notez que ces index sont consid\u00e9r\u00e9s comme des caract\u00e9ristiques discr\u00e8tes. En r\u00e8gle g\u00e9n\u00e9rale, ces derni\u00e8res sont encore converties en repr\u00e9sentations one-hot, mais cette op\u00e9ration est effectu\u00e9e de mani\u00e8re transparente.\n",
        "\n",
        "Pour d\u00e9finir des colonnes de caract\u00e9ristiques pour des caract\u00e9ristiques binaires, vous pouvez utiliser [`bucketized_column`] (https://www.tensorflow.org/api_docs/python/tf/feature_column/bucketized_column) au lieu de `numeric_column`. Cette fonction prend une colonne num\u00e9rique comme entr\u00e9e et la transforme en une caract\u00e9ristique binaire \u00e0 l'aide des limites de cat\u00e9gorie indiqu\u00e9es dans l'argument `boundaries`. Le code suivant d\u00e9finit des colonnes de caract\u00e9ristiques binaires pour `households` et `longitude`\u00a0; la fonction `get_quantile_based_boundaries` calcule les limites sur la base de quantiles, de sorte que chaque cat\u00e9gorie contienne un nombre \u00e9gal d'\u00e9l\u00e9ments."
      ]
    },
    {
      "metadata": {
        "id": "cc9qZrtRy-ED",
        "colab_type": "code",
        "colab": {
          "autoexec": {
            "startup": false,
            "wait_interval": 0
          }
        }
      },
      "source": [
        "def get_quantile_based_boundaries(feature_values, num_buckets):\n",
        "  boundaries = np.arange(1.0, num_buckets) / num_buckets\n",
        "  quantiles = feature_values.quantile(boundaries)\n",
        "  return [quantiles[q] for q in quantiles.keys()]\n",
        "\n",
        "# Divide households into 7 buckets.\n",
        "households = tf.feature_column.numeric_column(\"households\")\n",
        "bucketized_households = tf.feature_column.bucketized_column(\n",
        "  households, boundaries=get_quantile_based_boundaries(\n",
        "    california_housing_dataframe[\"households\"], 7))\n",
        "\n",
        "# Divide longitude into 10 buckets.\n",
        "longitude = tf.feature_column.numeric_column(\"longitude\")\n",
        "bucketized_longitude = tf.feature_column.bucketized_column(\n",
        "  longitude, boundaries=get_quantile_based_boundaries(\n",
        "    california_housing_dataframe[\"longitude\"], 10))"
      ],
      "cell_type": "code",
      "execution_count": 0,
      "outputs": []
    },
    {
      "metadata": {
        "id": "U-pQDAa0MeN3",
        "colab_type": "text"
      },
      "cell_type": "markdown",
      "source": [
        " ## T\u00e2che\u00a01\u00a0: Apprendre le mod\u00e8le sur des colonnes de caract\u00e9ristiques binaires\n",
        "**Effectuez un binning sur toutes les caract\u00e9ristiques valoris\u00e9es r\u00e9elles de l'exemple, entra\u00eenez le mod\u00e8le et constatez si cela permet d'am\u00e9liorer les r\u00e9sultats.**\n",
        "\n",
        "Dans le bloc de code qui pr\u00e9c\u00e8de, deux colonnes valoris\u00e9es r\u00e9elles (en l'occurrence `households` et `longitude`) ont \u00e9t\u00e9 transform\u00e9es en colonnes de caract\u00e9ristiques binaires. Votre t\u00e2che consiste \u00e0 effectuer un binning sur les autres colonnes, puis d'ex\u00e9cuter le code pour entra\u00eener le mod\u00e8le. Plusieurs m\u00e9thodes heuristiques peuvent \u00eatre utilis\u00e9es pour trouver l'intervalle des cat\u00e9gories. Dans cet exercice, vous allez utiliser une technique bas\u00e9e sur des quantiles. Cette derni\u00e8re choisit les limites de cat\u00e9gorie, de telle sorte que chaque cat\u00e9gorie poss\u00e8de le m\u00eame nombre d'exemples."
      ]
    },
    {
      "metadata": {
        "id": "YFXV9lyMLedy",
        "colab_type": "code",
        "colab": {
          "autoexec": {
            "startup": false,
            "wait_interval": 0
          }
        }
      },
      "source": [
        "def construct_feature_columns():\n",
        "  \"\"\"Construct the TensorFlow Feature Columns.\n",
        "\n",
        "  Returns:\n",
        "    A set of feature columns\n",
        "  \"\"\" \n",
        "  households = tf.feature_column.numeric_column(\"households\")\n",
        "  longitude = tf.feature_column.numeric_column(\"longitude\")\n",
        "  latitude = tf.feature_column.numeric_column(\"latitude\")\n",
        "  housing_median_age = tf.feature_column.numeric_column(\"housing_median_age\")\n",
        "  median_income = tf.feature_column.numeric_column(\"median_income\")\n",
        "  rooms_per_person = tf.feature_column.numeric_column(\"rooms_per_person\")\n",
        "  \n",
        "  # Divide households into 7 buckets.\n",
        "  bucketized_households = tf.feature_column.bucketized_column(\n",
        "    households, boundaries=get_quantile_based_boundaries(\n",
        "      training_examples[\"households\"], 7))\n",
        "\n",
        "  # Divide longitude into 10 buckets.\n",
        "  bucketized_longitude = tf.feature_column.bucketized_column(\n",
        "    longitude, boundaries=get_quantile_based_boundaries(\n",
        "      training_examples[\"longitude\"], 10))\n",
        "\n",
        "  #\n",
        "  # YOUR CODE HERE: bucketize the following columns, following the example above:\n",
        "  #\n",
        "  bucketized_latitude = \n",
        "  bucketized_housing_median_age = \n",
        "  bucketized_median_income =\n",
        "  bucketized_rooms_per_person =\n",
        "  \n",
        "  feature_columns = set([\n",
        "    bucketized_longitude,\n",
        "    bucketized_latitude,\n",
        "    bucketized_housing_median_age,\n",
        "    bucketized_households,\n",
        "    bucketized_median_income,\n",
        "    bucketized_rooms_per_person])\n",
        "  \n",
        "  return feature_columns\n"
      ],
      "cell_type": "code",
      "execution_count": 0,
      "outputs": []
    },
    {
      "metadata": {
        "id": "0FfUytOTNJhL",
        "colab_type": "code",
        "colab": {
          "autoexec": {
            "startup": false,
            "wait_interval": 0
          }
        }
      },
      "source": [
        "_ = train_model(\n",
        "    learning_rate=1.0,\n",
        "    steps=500,\n",
        "    batch_size=100,\n",
        "    feature_columns=construct_feature_columns(),\n",
        "    training_examples=training_examples,\n",
        "    training_targets=training_targets,\n",
        "    validation_examples=validation_examples,\n",
        "    validation_targets=validation_targets)"
      ],
      "cell_type": "code",
      "execution_count": 0,
      "outputs": []
    },
    {
      "metadata": {
        "id": "ZTDHHM61NPTw",
        "colab_type": "text"
      },
      "cell_type": "markdown",
      "source": [
        " ### Solution\n",
        "\n",
        "Cliquez ci-dessous pour afficher une solution."
      ]
    },
    {
      "metadata": {
        "id": "JQHnUhL_NRwA",
        "colab_type": "text"
      },
      "cell_type": "markdown",
      "source": [
        " Vous vous demandez peut-\u00eatre comment d\u00e9terminer le nombre de cat\u00e9gories \u00e0 utiliser. Cela d\u00e9pend, bien \u00e9videmment, des donn\u00e9es. Dans le cas pr\u00e9sent, nous avons simplement s\u00e9lectionn\u00e9 des valeurs arbitraires afin d'obtenir un mod\u00e8le de taille raisonnable."
      ]
    },
    {
      "metadata": {
        "id": "Ro5civQ3Ngh_",
        "colab_type": "code",
        "colab": {
          "autoexec": {
            "startup": false,
            "wait_interval": 0
          }
        }
      },
      "source": [
        "def construct_feature_columns():\n",
        "  \"\"\"Construct the TensorFlow Feature Columns.\n",
        "\n",
        "  Returns:\n",
        "    A set of feature columns\n",
        "  \"\"\" \n",
        "  households = tf.feature_column.numeric_column(\"households\")\n",
        "  longitude = tf.feature_column.numeric_column(\"longitude\")\n",
        "  latitude = tf.feature_column.numeric_column(\"latitude\")\n",
        "  housing_median_age = tf.feature_column.numeric_column(\"housing_median_age\")\n",
        "  median_income = tf.feature_column.numeric_column(\"median_income\")\n",
        "  rooms_per_person = tf.feature_column.numeric_column(\"rooms_per_person\")\n",
        "  \n",
        "  # Divide households into 7 buckets.\n",
        "  bucketized_households = tf.feature_column.bucketized_column(\n",
        "    households, boundaries=get_quantile_based_boundaries(\n",
        "      training_examples[\"households\"], 7))\n",
        "\n",
        "  # Divide longitude into 10 buckets.\n",
        "  bucketized_longitude = tf.feature_column.bucketized_column(\n",
        "    longitude, boundaries=get_quantile_based_boundaries(\n",
        "      training_examples[\"longitude\"], 10))\n",
        "  \n",
        "  # Divide latitude into 10 buckets.\n",
        "  bucketized_latitude = tf.feature_column.bucketized_column(\n",
        "    latitude, boundaries=get_quantile_based_boundaries(\n",
        "      training_examples[\"latitude\"], 10))\n",
        "\n",
        "  # Divide housing_median_age into 7 buckets.\n",
        "  bucketized_housing_median_age = tf.feature_column.bucketized_column(\n",
        "    housing_median_age, boundaries=get_quantile_based_boundaries(\n",
        "      training_examples[\"housing_median_age\"], 7))\n",
        "  \n",
        "  # Divide median_income into 7 buckets.\n",
        "  bucketized_median_income = tf.feature_column.bucketized_column(\n",
        "    median_income, boundaries=get_quantile_based_boundaries(\n",
        "      training_examples[\"median_income\"], 7))\n",
        "  \n",
        "  # Divide rooms_per_person into 7 buckets.\n",
        "  bucketized_rooms_per_person = tf.feature_column.bucketized_column(\n",
        "    rooms_per_person, boundaries=get_quantile_based_boundaries(\n",
        "      training_examples[\"rooms_per_person\"], 7))\n",
        "  \n",
        "  feature_columns = set([\n",
        "    bucketized_longitude,\n",
        "    bucketized_latitude,\n",
        "    bucketized_housing_median_age,\n",
        "    bucketized_households,\n",
        "    bucketized_median_income,\n",
        "    bucketized_rooms_per_person])\n",
        "  \n",
        "  return feature_columns"
      ],
      "cell_type": "code",
      "execution_count": 0,
      "outputs": []
    },
    {
      "metadata": {
        "id": "RNgfYk6OO8Sy",
        "colab_type": "code",
        "colab": {
          "autoexec": {
            "startup": false,
            "wait_interval": 0
          }
        }
      },
      "source": [
        "_ = train_model(\n",
        "    learning_rate=1.0,\n",
        "    steps=500,\n",
        "    batch_size=100,\n",
        "    feature_columns=construct_feature_columns(),\n",
        "    training_examples=training_examples,\n",
        "    training_targets=training_targets,\n",
        "    validation_examples=validation_examples,\n",
        "    validation_targets=validation_targets)"
      ],
      "cell_type": "code",
      "execution_count": 0,
      "outputs": []
    },
    {
      "metadata": {
        "id": "AFJ1qoZPlQcs",
        "colab_type": "text"
      },
      "cell_type": "markdown",
      "source": [
        " ## Croisement de caract\u00e9ristiques\n",
        "\n",
        "Le croisement de plusieurs caract\u00e9ristiques est une m\u00e9thode astucieuse pour l'apprentissage des relations non\u00a0lin\u00e9aires \u00e0 l'aide d'un mod\u00e8le lin\u00e9aire. Dans le cas pr\u00e9sent, si l'on se contente d'utiliser la caract\u00e9ristique `latitude` pour l'apprentissage, il se peut que le mod\u00e8le apprenne que les \u00eelots urbains situ\u00e9s \u00e0 une latitude donn\u00e9e (ou dans une plage de latitudes sp\u00e9cifique, \u00e9tant donn\u00e9 qu'un binning a \u00e9t\u00e9 effectu\u00e9) sont davantage susceptibles d'\u00eatre plus chers que d'autres. Il en va de m\u00eame pour la caract\u00e9ristique `longitude`. Cependant si l'on croise la caract\u00e9ristique `longitude` avec `latitude`, la caract\u00e9ristique crois\u00e9e repr\u00e9sente un \u00eelot urbain bien d\u00e9fini. Si le mod\u00e8le apprend que certains \u00eelots (situ\u00e9s dans une plage de latitudes et de longitudes) sont plus susceptibles d'\u00eatre plus chers que d'autres, cela constitue un signal plus fort que deux caract\u00e9ristiques prises en compte s\u00e9par\u00e9ment.\n",
        "\n",
        "Pour le moment, seules les caract\u00e9ristiques discr\u00e8tes peuvent faire l'objet de croisements dans l'API des colonnes de caract\u00e9ristiques. Pour croiser deux valeurs continues, comme `latitude` ou `longitude`, un binning peut \u00eatre effectu\u00e9.\n",
        "\n",
        "Si vous croisez les caract\u00e9ristiques `latitude` et `longitude` (supposons, par exemple, que la caract\u00e9ristique `longitude` ait \u00e9t\u00e9 plac\u00e9e dans `2`\u00a0cat\u00e9gories et que `latitude` poss\u00e8de `3`\u00a0cat\u00e9gories), vous obtiendrez six caract\u00e9ristiques binaires crois\u00e9es. Chacune d'elles recevra alors sa propre pond\u00e9ration lors de l'apprentissage du mod\u00e8le."
      ]
    },
    {
      "metadata": {
        "id": "-Rk0c1oTYaVH",
        "colab_type": "text"
      },
      "cell_type": "markdown",
      "source": [
        " ## T\u00e2che\u00a02\u00a0: Entra\u00eener le mod\u00e8le \u00e0 l'aide de croisements de caract\u00e9ristiques\n",
        "\n",
        "**Ajoutez un croisement des caract\u00e9ristiques `longitude` et `latitude` \u00e0 votre mod\u00e8le, entra\u00eenez le mod\u00e8le et voyez si cela permet d'am\u00e9liorer les r\u00e9sultats.**\n",
        "\n",
        "Consultez la section de la documentation de l'API\u00a0TensorFlow relative \u00e0 la fonction [`crossed_column()`](https://www.tensorflow.org/api_docs/python/tf/feature_column/crossed_column) afin de construire la colonne de caract\u00e9ristiques pour votre croisement. Utilisez une valeur `hash_bucket_size` de `1000`."
      ]
    },
    {
      "metadata": {
        "id": "-eYiVEGeYhUi",
        "colab_type": "code",
        "colab": {
          "autoexec": {
            "startup": false,
            "wait_interval": 0
          }
        },
        "cellView": "both"
      },
      "source": [
        "def construct_feature_columns():\n",
        "  \"\"\"Construct the TensorFlow Feature Columns.\n",
        "\n",
        "  Returns:\n",
        "    A set of feature columns\n",
        "  \"\"\" \n",
        "  households = tf.feature_column.numeric_column(\"households\")\n",
        "  longitude = tf.feature_column.numeric_column(\"longitude\")\n",
        "  latitude = tf.feature_column.numeric_column(\"latitude\")\n",
        "  housing_median_age = tf.feature_column.numeric_column(\"housing_median_age\")\n",
        "  median_income = tf.feature_column.numeric_column(\"median_income\")\n",
        "  rooms_per_person = tf.feature_column.numeric_column(\"rooms_per_person\")\n",
        "  \n",
        "  # Divide households into 7 buckets.\n",
        "  bucketized_households = tf.feature_column.bucketized_column(\n",
        "    households, boundaries=get_quantile_based_boundaries(\n",
        "      training_examples[\"households\"], 7))\n",
        "\n",
        "  # Divide longitude into 10 buckets.\n",
        "  bucketized_longitude = tf.feature_column.bucketized_column(\n",
        "    longitude, boundaries=get_quantile_based_boundaries(\n",
        "      training_examples[\"longitude\"], 10))\n",
        "  \n",
        "  # Divide latitude into 10 buckets.\n",
        "  bucketized_latitude = tf.feature_column.bucketized_column(\n",
        "    latitude, boundaries=get_quantile_based_boundaries(\n",
        "      training_examples[\"latitude\"], 10))\n",
        "\n",
        "  # Divide housing_median_age into 7 buckets.\n",
        "  bucketized_housing_median_age = tf.feature_column.bucketized_column(\n",
        "    housing_median_age, boundaries=get_quantile_based_boundaries(\n",
        "      training_examples[\"housing_median_age\"], 7))\n",
        "  \n",
        "  # Divide median_income into 7 buckets.\n",
        "  bucketized_median_income = tf.feature_column.bucketized_column(\n",
        "    median_income, boundaries=get_quantile_based_boundaries(\n",
        "      training_examples[\"median_income\"], 7))\n",
        "  \n",
        "  # Divide rooms_per_person into 7 buckets.\n",
        "  bucketized_rooms_per_person = tf.feature_column.bucketized_column(\n",
        "    rooms_per_person, boundaries=get_quantile_based_boundaries(\n",
        "      training_examples[\"rooms_per_person\"], 7))\n",
        "  \n",
        "  # YOUR CODE HERE: Make a feature column for the long_x_lat feature cross\n",
        "  long_x_lat = \n",
        "  \n",
        "  feature_columns = set([\n",
        "    bucketized_longitude,\n",
        "    bucketized_latitude,\n",
        "    bucketized_housing_median_age,\n",
        "    bucketized_households,\n",
        "    bucketized_median_income,\n",
        "    bucketized_rooms_per_person,\n",
        "    long_x_lat])\n",
        "  \n",
        "  return feature_columns"
      ],
      "cell_type": "code",
      "execution_count": 0,
      "outputs": []
    },
    {
      "metadata": {
        "id": "xZuZMp3EShkM",
        "colab_type": "code",
        "colab": {
          "autoexec": {
            "startup": false,
            "wait_interval": 0
          }
        }
      },
      "source": [
        "_ = train_model(\n",
        "    learning_rate=1.0,\n",
        "    steps=500,\n",
        "    batch_size=100,\n",
        "    feature_columns=construct_feature_columns(),\n",
        "    training_examples=training_examples,\n",
        "    training_targets=training_targets,\n",
        "    validation_examples=validation_examples,\n",
        "    validation_targets=validation_targets)"
      ],
      "cell_type": "code",
      "execution_count": 0,
      "outputs": []
    },
    {
      "metadata": {
        "id": "0i7vGo9PTaZl",
        "colab_type": "text"
      },
      "cell_type": "markdown",
      "source": [
        " ### Solution\n",
        "\n",
        "Cliquez ci-dessous pour afficher la solution."
      ]
    },
    {
      "metadata": {
        "id": "3tAWu8qSTe2v",
        "colab_type": "code",
        "colab": {
          "autoexec": {
            "startup": false,
            "wait_interval": 0
          }
        }
      },
      "source": [
        "def construct_feature_columns():\n",
        "  \"\"\"Construct the TensorFlow Feature Columns.\n",
        "\n",
        "  Returns:\n",
        "    A set of feature columns\n",
        "  \"\"\" \n",
        "  households = tf.feature_column.numeric_column(\"households\")\n",
        "  longitude = tf.feature_column.numeric_column(\"longitude\")\n",
        "  latitude = tf.feature_column.numeric_column(\"latitude\")\n",
        "  housing_median_age = tf.feature_column.numeric_column(\"housing_median_age\")\n",
        "  median_income = tf.feature_column.numeric_column(\"median_income\")\n",
        "  rooms_per_person = tf.feature_column.numeric_column(\"rooms_per_person\")\n",
        "  \n",
        "  # Divide households into 7 buckets.\n",
        "  bucketized_households = tf.feature_column.bucketized_column(\n",
        "    households, boundaries=get_quantile_based_boundaries(\n",
        "      training_examples[\"households\"], 7))\n",
        "\n",
        "  # Divide longitude into 10 buckets.\n",
        "  bucketized_longitude = tf.feature_column.bucketized_column(\n",
        "    longitude, boundaries=get_quantile_based_boundaries(\n",
        "      training_examples[\"longitude\"], 10))\n",
        "  \n",
        "  # Divide latitude into 10 buckets.\n",
        "  bucketized_latitude = tf.feature_column.bucketized_column(\n",
        "    latitude, boundaries=get_quantile_based_boundaries(\n",
        "      training_examples[\"latitude\"], 10))\n",
        "\n",
        "  # Divide housing_median_age into 7 buckets.\n",
        "  bucketized_housing_median_age = tf.feature_column.bucketized_column(\n",
        "    housing_median_age, boundaries=get_quantile_based_boundaries(\n",
        "      training_examples[\"housing_median_age\"], 7))\n",
        "  \n",
        "  # Divide median_income into 7 buckets.\n",
        "  bucketized_median_income = tf.feature_column.bucketized_column(\n",
        "    median_income, boundaries=get_quantile_based_boundaries(\n",
        "      training_examples[\"median_income\"], 7))\n",
        "  \n",
        "  # Divide rooms_per_person into 7 buckets.\n",
        "  bucketized_rooms_per_person = tf.feature_column.bucketized_column(\n",
        "    rooms_per_person, boundaries=get_quantile_based_boundaries(\n",
        "      training_examples[\"rooms_per_person\"], 7))\n",
        "  \n",
        "  # YOUR CODE HERE: Make a feature column for the long_x_lat feature cross\n",
        "  long_x_lat = tf.feature_column.crossed_column(\n",
        "  set([bucketized_longitude, bucketized_latitude]), hash_bucket_size=1000) \n",
        "  \n",
        "  feature_columns = set([\n",
        "    bucketized_longitude,\n",
        "    bucketized_latitude,\n",
        "    bucketized_housing_median_age,\n",
        "    bucketized_households,\n",
        "    bucketized_median_income,\n",
        "    bucketized_rooms_per_person,\n",
        "    long_x_lat])\n",
        "  \n",
        "  return feature_columns"
      ],
      "cell_type": "code",
      "execution_count": 0,
      "outputs": []
    },
    {
      "metadata": {
        "id": "-_vvNYIyTtPC",
        "colab_type": "code",
        "colab": {
          "autoexec": {
            "startup": false,
            "wait_interval": 0
          }
        }
      },
      "source": [
        "_ = train_model(\n",
        "    learning_rate=1.0,\n",
        "    steps=500,\n",
        "    batch_size=100,\n",
        "    feature_columns=construct_feature_columns(),\n",
        "    training_examples=training_examples,\n",
        "    training_targets=training_targets,\n",
        "    validation_examples=validation_examples,\n",
        "    validation_targets=validation_targets)"
      ],
      "cell_type": "code",
      "execution_count": 0,
      "outputs": []
    },
    {
      "metadata": {
        "id": "ymlHJ-vrhLZw",
        "colab_type": "text"
      },
      "cell_type": "markdown",
      "source": [
        " ## Exercice facultatif\u00a0: Essayer d'autres caract\u00e9ristiques synth\u00e9tiques\n",
        "\n",
        "Jusqu'\u00e0 pr\u00e9sent, nous nous en sommes tenus \u00e0 des croisements de caract\u00e9ristiques et de colonnes binaires simples. Cependant, il existe bien d'autres combinaisons susceptibles d'am\u00e9liorer les r\u00e9sultats. Vous pouvez, par exemple, croiser plusieurs colonnes. Que se passe-t-il si vous faites varier le nombre de cat\u00e9gories\u00a0? Quelles sont les autres caract\u00e9ristiques synth\u00e9tiques auxquelles vous pensez\u00a0? Permettent-elles d'am\u00e9liorer le mod\u00e8le\u00a0?"
      ]
    }
  ]
}
